// Canvas class for drawing
//

#include <stdlib.h>
#include <string.h>
#include "Common/Debug.h"
#include "Common/Macros.h"
#include "Common/Memory.h"
#include "Color.h"
#include "Canvas.h"

// Get clipping area macro
#define  CLIP_LEFT          (canvas->Left)
#define  CLIP_TOP           (canvas->Top)
#define  CLIP_RIGHT			(canvas->Right)
#define  CLIP_BOTTOM		(canvas->Bottom)

/////////////////////////////////////////////////////////////////////////////
// Canvas maintaining functions

/* Create a canvas object from memory allocating internally */
int g_CreateCanvas(TCanvas *canvas, int width, int height)
{
	ASSERT_DEBUG(canvas);

	canvas->pData = ut_malloc(PIXEL_SIZE * width * height);

	ASSERT_ALWAYS(canvas->pData);

	canvas->Width = width;
	canvas->Height = height;
	canvas->BytesPerRow = PIXEL_TO_BYTE(width);
	canvas->Type = 1;
    canvas->ColorSpace = 0;

    g_ResetClip(canvas);

	LOG("g_CreateCanvas: %d x %d\n", width, height);

	return 1;
}

/* Create a canvas object from other continuous buffer */
int g_DefineCanvas(TCanvas *canvas, char *buff, int width, int height, int bytesPerRow)
{
	ASSERT_DEBUG(canvas);
	ASSERT_DEBUG(buff);

	canvas->pData = (UInt8*)buff;
	canvas->Width = width;
	canvas->Height = height;
	canvas->BytesPerRow = bytesPerRow;
	canvas->Type = 2;
    canvas->ColorSpace = 0;

    g_ResetClip(canvas);

	LOG("g_DefineCanvas: %d x %d\n", width, height);

	return 1;
}

/* Create a canvas object from other canvas, drawing may affect both */
int g_CloneCanvas(TCanvas *canvas, const TCanvas *parent, int l, int t, int r, int b)
{
	ASSERT_DEBUG(canvas);
    ASSERT_DEBUG(parent && parent->Type);

    if (l >= parent->Width || t >= parent->Height || r < 0 || b < 0)
    {
        TRACE("CloneCanvas: bad area!\n");
        return 0;
    }

    if (l < 0)  l = 0;
    if (t < 0)  t = 0;
    if (r >= parent->Width ) r = parent->Width - 1;
    if (b >= parent->Height) b = parent->Height - 1; 

	canvas->pData = parent->pData + (t * parent->BytesPerRow) + PIXEL_TO_BYTE(l);
	canvas->Width = r - l + 1;
	canvas->Height = b - t + 1;
	canvas->BytesPerRow = parent->BytesPerRow;
	canvas->Type = 2;

    g_ResetClip(canvas);

	LOG("g_CloneCanvas: (%d, %d)-(%d, %d)\n", l, t, r, b);

	return 1;
}

/* Destroy a canvas */
/* NOTE: destroy a parent canvas and still use its clone will cause disaster!
		 need to implement a 'reference mechanism' later -OB
 */
void g_DestroyCanvas(TCanvas *canvas)
{
	ASSERT_DEBUG(canvas);
    
    if (canvas->Type == 1)
        ut_free(canvas->pData);

    memset(canvas, 0, sizeof(TCanvas));
//  canvas->Type = 0;       // set to invalid
}


/* Perform a bit block transfer (blit) from a canvas to another */
/* NOTE: 1. only do a simple width/height clipping
         2. may cause error when copy between a cloned canvas and its parent,
            should fix this later -OB
   PLAN: 1. write a NC(no-clip) version for speed? 
 */
void g_BltCanvas(TCanvas *dest, int x, int y, const TCanvas *src, int l, int t, int r, int b)
{
    int width, height;
    int row_s, row_d;
    UInt8 *ptr_s, *ptr_d;

    ASSERT_DEBUG(src && dest);

    width  = r - l + 1;
    height = b - t + 1;

    // Setup source
    row_s = src->BytesPerRow;
    ptr_s = src->pData + (t * row_s) + PIXEL_TO_BYTE(l);

    // Setup destination
    row_d = dest->BytesPerRow;
    ptr_d = dest->pData + (y * row_d) + PIXEL_TO_BYTE(x);

    if ((x + width) > dest->Width)
        width = dest->Width - x;

    if ((y + height) > dest->Height)
        height = dest->Height - y;

    width = PIXEL_TO_BYTE(width);

    // Copy data
    if (src != dest)    // !!! may still overlap if one was cloned from another
    {                   
        while (height--)
        {
            memcpy(ptr_d, ptr_s, width);
            ptr_s += row_s;
            ptr_d += row_d;
        }
    }
    else
    {
        if (t >= y || b < y)
        {
            while (height--)
            {
                memmove(ptr_d, src, width);
                ptr_s += row_s;
                ptr_d += row_s;
            }
        }
        else
        {
            ptr_s = ptr_s + (height - 1) * row_s;
            ptr_d = ptr_d + (height - 1) * row_s;

            while (height--)
            {
                memcpy(ptr_d, ptr_s, width);
                ptr_s -= row_s;
                ptr_d -= row_s;
            }
        }
    }
}

/* Copy a canvas to another (i.e. g_BltCanvas full src to dest's 0,0) */
/* NOTE: may cause error when copy between a cloned canvas and its parent,
         should fix this later -OB
 */
void g_CopyCanvas(TCanvas *dest, const TCanvas *src)
{
    int width, height;
    UInt8 *ptr_s, *ptr_d;
    ASSERT_DEBUG(src && dest && src != dest);

    ptr_s = src->pData;
    ptr_d = dest->pData;

    width = min(src->Width, dest->Width);
	height = min(src->Height, dest->Height);

    width = PIXEL_TO_BYTE(width);

    // Copy data
    while (height--)
    {
        memcpy(ptr_d, ptr_s, width);
        ptr_s += src->BytesPerRow;
        ptr_d += dest->BytesPerRow;
    }   
}

/* Double a canvas on the fly to another */
void g_DblCanvas(TCanvas *dest, const TCanvas *src)
{
    int width, height, i;
    UInt8 *ptr_s, *ptr_d;
#if defined __RGB16
	UInt16 *s;
	UInt32 *d;
	UInt32 data;
#elif defined __RGB24
	UInt8 *s;
	UInt8 *d;
	UInt8 data;
#endif

    ASSERT_DEBUG(src && dest && src != dest);

    ptr_s = src->pData;
    ptr_d = dest->pData;

/*  assume dest canvas is large enough for src doubling
    width = src->Width;
	height = src->Height;

	ASSERT_DEBUG(width <= dest->Width / 2 && height <= dest->Height / 2);
*/
    width = dest->Width / 2;
	height = dest->Height / 2;

	if (width < src->Width) width = src->Width;
	if (height < src->Height) height = src->Height;

    // Copy data
    while (height--)
    {
#if defined __RGB16
 		s = (UInt16*) ptr_s;
		d = (UInt32*) ptr_d;

		for (i = 0; i < width; i++)
		{
			data = *s;
			data |= (data << 16);
			*d = data;
			s++;
			d++;
		}
#elif defined __RGB24
 		s = ptr_s;
		d = ptr_d;

		for (i = 0; i < width; i++)
		{
			data = *s;
			*d = data;
			*(d+3) = data;			
			s++;
			d++;

			data = *s;
			*d = data;
			*(d+3) = data;			
			s++;
			d++;

			data = *s;
			*d = data;
			*(d+3) = data;			
			s++;
			d+=4;
		}
#endif
		memcpy(ptr_d + dest->BytesPerRow, ptr_d, (width * PIXEL_SIZE) << 1);
		ptr_s += src->BytesPerRow;
		ptr_d += (dest->BytesPerRow << 1);
    }  
}

// void dev_DrawRGBPixel(int x, int y, int r, int g, int b);

/* Copy a canvas to another w/ YUV transform */
void g_CopyCanvas_YUV(TCanvas *dest, const TCanvas *src)
{
	UInt8* data;
	UInt16* prgb;
	int width, height;
	int x, y;

	UInt8*	yplane, *uvplane, *py, *puv;

	int R, G, B;
	int _Y, _U, _V;

	data = src->pData;	
	width = src->Width;		
	height = src->Height;	

	yplane = dest->pData;
	uvplane = dest->pData + dest->Width * dest->Height;

	for (y = 0; y < height; y++)
	{
		prgb = (UInt16*) data;
		py = yplane;
		puv = uvplane;
 	
		for (x = 0; x < width; x++)
		{
			R = _GetRValue(*prgb);	
			G = _GetGValue(*prgb);	
			B = _GetBValue(*prgb);	
		
//			dev_DrawRGBPixel(x, y, r, g, b);

			_Y =  77*R/256 + 150*G/256 +  29*B/256;

			*py = (unsigned char) _Y;

// mixed UV version
/*			_U = -44*R/256 -  87*G/256 + 131*B/256 + 128;
			_V = 131*R/256 - 110*G/256 -  21*B/256 + 128;

			*puv = (_U + *puv) >> 1;
			*(puv + 1) = (_V + *(puv+1)) >> 1;

			if (x & 1)
				puv += 2;
*/
			if ((x & 1) && (y & 1))
			{
				_U = -44*R/256 -  87*G/256 + 131*B/256 + 128;
				_V = 131*R/256 - 110*G/256 -  21*B/256 + 128;

				*puv = (unsigned char) _U;
				*(puv + 1) = (unsigned char) _V;

				puv += 2;
			}

			prgb++;
			py++;
		} 

		data += src->BytesPerRow;
		yplane += dest->Width;
		if (y & 1)
			uvplane += dest->Width;
	}
}

/* Clear a canvas */
void g_ClearCanvas(TCanvas *dest)
{
	memset(dest->pData, 0, dest->Width * dest->Height * PIXEL_SIZE); 
}

/* Clear a YUV canvas */
void g_ClearCanvas_YUV(TCanvas *dest)
{
	int half = dest->Width * dest->Height;
	// Y plane
	memset(dest->pData, 0, half);
	// UV plane
	memset(dest->pData + half, 0x80, half);
}

/* Set a clipping area to a canvas */
void g_SetClip(TCanvas *canvas, int l, int t, int r, int b)
{
    if (l < 0)  l = 0;
    if (t < 0)  t = 0;
    if (r >= canvas->Width) r = canvas->Width - 1;
    if (b >= canvas->Height) b = canvas->Height - 1;
    
    CLIP_LEFT   = l;
    CLIP_TOP    = t;
    CLIP_RIGHT  = r;
    CLIP_BOTTOM = b;
}

/* Get the clipping area of a canvas */
void g_GetClip(TCanvas *canvas, int *l, int *t, int *r, int *b)
{
    *l = CLIP_LEFT;
    *t = CLIP_TOP;
    *r = CLIP_RIGHT;
    *b = CLIP_BOTTOM;
}

/* Modify the clipping area of a canvas by offset */
void g_ModifyClip(TCanvas *canvas, int l, int t, int r, int b)
{
    l += CLIP_LEFT;
    t += CLIP_TOP;
    r += CLIP_RIGHT;
    b += CLIP_BOTTOM;

    g_SetClip(canvas, l, t, r, b);
}

/* Rest the clipping area of a canvas to default (aka full canvas area) */
void g_ResetClip(TCanvas *canvas)
{
    CLIP_LEFT   = 0;
    CLIP_TOP    = 0;
    CLIP_RIGHT  = canvas->Width - 1;
    CLIP_BOTTOM = canvas->Height - 1;
}

#ifdef _BF533
#include "DrawTool.h"
extern	unsigned char navi_status;



void g_CopyCanvas_YUV_BF533_part(TCanvas *dest, const TCanvas *src,TRect rect)
{
	unsigned char *rgb_base  = (unsigned char *)src->pData;
	unsigned char *ycrcb_base = (unsigned char *)dest->pData;
	unsigned char *odd_yuv;
	unsigned char *even_yuv;
	unsigned char *rgb_ptr;
	int oddline, evenline,rgbline;
	int tmpWidth, tmpHeight;
	
	unsigned int height1,height2,width;
	
	/*if ( !(rect.right%2) )
		rect.right ++;
	if ( rect.bottom%2)
		rect.bottom ++;
		*/
	rect.right = ((rect.right%2) ? rect.right : (++rect.right));
	rect.bottom = ((rect.bottom%2) ? (++rect.bottom) : rect.bottom);		
	height1 = (unsigned int) rect.top;
	height2 = (unsigned int) rect.bottom; 
	width = (unsigned int) (rect.right - rect.left) +1; 
	
	tmpWidth = min(width, SCREEN_WIDTH);
	height1 = min(height1, SCREEN_HEIGHT);
	height2 = min(height2, SCREEN_HEIGHT);

	//printf ("Trect is xy: %d,%d, xy:%d,%d \n",rect.left,rect.top,rect.right,rect.bottom);
	if(!navi_status)
	{
		for (;height1 <= height2; height1 ++)
		{			
			if( (height1%2))
				{
					evenline = FIELD2_AV_START + height1/2;
					even_yuv = (unsigned char *)((ycrcb_base + (evenline * YCBCR_WIDTH))+HB_LENGTH+8 + rect.left*2);
					rgb_ptr = (unsigned char *)(rgb_base + (height1)*SCREEN_WIDTH*3 + (rect.left)*3);					
					rgb2yuv(rgb_ptr,even_yuv,tmpWidth);
				}
			else
				{
					oddline = FIELD1_AV_START + height1/2;
					odd_yuv= (unsigned char *)((ycrcb_base + (oddline * YCBCR_WIDTH))+HB_LENGTH+8 + rect.left*2);
					rgb_ptr = (unsigned char *)(rgb_base + (height1)*SCREEN_WIDTH*3 + (rect.left)*3);					
					rgb2yuv(rgb_ptr,odd_yuv,tmpWidth);
				}
		}		
	}
/*	unsigned int height1,height2,width;
	
	rect.left = 1;
	rect.top = 1;
	rect.right = 719;
	rect.bottom = 479;
		 
	height1 = (unsigned int) rect.top;
	height2 = (unsigned int) rect.bottom; 
	width = (unsigned int) (rect.right - rect.left); 
	
	tmpWidth = min(width, SCREEN_WIDTH);
	height1 = min(height1, SCREEN_HEIGHT);
	height2 = min(height2, SCREEN_HEIGHT);

	printf ("Trect is xy: %d,%d, xy:%d,%d \n",rect.left,rect.top,rect.right,rect.bottom);
	if(!navi_status)
	{
		for (;height1 <= height2; height1 ++)
		{			
			if( (height1%2))
				{
					evenline = FIELD2_AV_START + height1/2;
					even_yuv = (unsigned char *)((ycrcb_base + (evenline * YCBCR_WIDTH))+HB_LENGTH+8 + rect.left*2);
					rgb_ptr = (unsigned char *)(rgb_base + (height1-1)*SCREEN_WIDTH*3 + (rect.left)*3);
					printf ("in dt_Present_part height is %d_____evenline\n",height1);
					rgb2yuv(rgb_ptr,even_yuv,tmpWidth);
				}
			else
				{
					oddline = FIELD1_AV_START + height1/2;
					odd_yuv= (unsigned char *)((ycrcb_base + (oddline * YCBCR_WIDTH))+HB_LENGTH+8 + rect.left*2);
				rgb_ptr = (unsigned char *)(rgb_base + (height1-1)*SCREEN_WIDTH*3 + (rect.left)*3);
					printf ("in dt_Present_part height is %d_____oddline\n",height1);
					rgb2yuv(rgb_ptr,odd_yuv,tmpWidth);
				}
		}		
	}*/	
}


void g_CopyCanvas_YUV_BF533(TCanvas *dest, const TCanvas *src)
{
	unsigned char *rgb_base  = (unsigned char *)src->pData;
	unsigned char *ycrcb_base = (unsigned char *)dest->pData;
	unsigned char *odd_yuv;
	unsigned char *even_yuv;
	unsigned char *rgb_ptr;
	int oddline, evenline,rgbline;
	int tmpWidth, tmpHeight;
/*	
	unsigned int height1,height2,width;
	TRect rect;
	
	rect.left = 0;
	rect.top = 0;
	rect.right = 719;
	rect.bottom = 479;
		 
	height1 = (unsigned int) rect.top;
	height2 = (unsigned int) rect.bottom; 
	width = (unsigned int) (rect.right - rect.left) +1; 
	
	tmpWidth = min(width, SCREEN_WIDTH);
	height1 = min(height1, SCREEN_HEIGHT);
	height2 = min(height2, SCREEN_HEIGHT);

	printf ("Trect is xy: %d,%d, xy:%d,%d \n",rect.left,rect.top,rect.right,rect.bottom);
	if(!navi_status)
	{
		for (;height1 <= height2; height1 ++)
		{			
			if( (height1%2))
				{
					evenline = FIELD2_AV_START + height1/2;
					even_yuv = (unsigned char *)((ycrcb_base + (evenline * YCBCR_WIDTH))+HB_LENGTH+8 + rect.left*2);
					rgb_ptr = (unsigned char *)(rgb_base + (height1)*720*3 + (rect.left)*3);
					//printf ("in dt_Present_part height is %d_____evenline\n",height1);
					rgb2yuv(rgb_ptr,even_yuv,tmpWidth);
				}
			else
				{
					oddline = FIELD1_AV_START + height1/2;
					odd_yuv= (unsigned char *)((ycrcb_base + (oddline * YCBCR_WIDTH))+HB_LENGTH+8 + rect.left*2);
				rgb_ptr = (unsigned char *)(rgb_base + (height1)*720*3 + (rect.left)*3);
					//printf ("in dt_Present_part height is %d_____oddline\n",height1);
					rgb2yuv(rgb_ptr,odd_yuv,tmpWidth);
				}
		}		
	}
	*/
	
	tmpWidth = min(RGB_WIDTH, SCREEN_WIDTH);
	tmpHeight = min(RGB_HEIGHT, SCREEN_HEIGHT) / 2;

	//Bill modify
	if(!navi_status)
	{
		for (oddline = FIELD1_AV_START, evenline = FIELD2_AV_START, rgbline = 0;
		   ((oddline <= FIELD1_AV_END) && tmpHeight); oddline ++, evenline ++, tmpHeight--) {
		   	#if 1   ///org
			odd_yuv= (unsigned char *)((ycrcb_base + (oddline * YCBCR_WIDTH))+HB_LENGTH+8);
			rgb_ptr = (unsigned char *)(rgb_base + (rgbline++)*tmpWidth*3);

			rgb2yuv(rgb_ptr,odd_yuv,tmpWidth);

			even_yuv = (unsigned char *)((ycrcb_base + (evenline * YCBCR_WIDTH))+HB_LENGTH+8);
			rgb_ptr = (unsigned char *)(rgb_base + (rgbline++)*tmpWidth*3);

			rgb2yuv(rgb_ptr,even_yuv,tmpWidth);
			#else ///////////hangsheng 20080717
			even_yuv = (unsigned char *)((ycrcb_base + (evenline * YCBCR_WIDTH))+HB_LENGTH+8);			
			rgb_ptr = (unsigned char *)(rgb_base + (rgbline++)*tmpWidth*3);

			rgb2yuv(rgb_ptr,odd_yuv,tmpWidth);

			odd_yuv= (unsigned char *)((ycrcb_base + (oddline * YCBCR_WIDTH))+HB_LENGTH+8);
			rgb_ptr = (unsigned char *)(rgb_base + (rgbline++)*tmpWidth*3);
			
			rgb2yuv(rgb_ptr,even_yuv,tmpWidth);
			#endif
		}
	}
	
}

void g_ClearCanvas_YUV_BF533(TCanvas *dest)
{

}
#endif
